<?php
/**
 * Created by PhpStorm.
 * User: Appla
 * Date: 2017/4/13
 * Time: 10:09
 */

namespace test;

require __DIR__.'/../app/cron/header.php';

use artisan\http;
use DateTime;
use InvalidArgumentException;
use model\Address\DbCache;
use model\scan\scan;
use service\address\CachePool;
use util\DbUtil;

/**
 * 获取里的错误数据log
 * Class FailedToGetSto1stCodeLogTest
 * @package test
 */
class FailedToGetSto1stCodeLogTest
{

    /**
     * DB configs
     */
    const DB_NAME = 'remoteDts';
    const TABLE_NAME_PREFIX = 'error_log';

//    const DB_CONFIG = [
//        self::DB_NAME => [
//            'host' => '10.168.177.85',
//            'user' => 'reader',
//            'pass' => 'Ut90CVgeOwwwfL%d',
//            'db' => 'kd_address',
//            'port' => '3311',
//            'slaves' => []
//        ],
//    ];

    const API_URI = 'http://shop2.core.kuaidihelp.com/v1/Address/querySortingCode';

    /**
     * 模板
     */
    const QUERY_LOG_TPL = 'SELECT * FROM `%s` WHERE `service_name` = \'addressLib\' AND breakpoint = \'failedToGetStoCodes@serviceaddressAddrMatch::query\' AND id > %d  LIMIT %d';

    const QUERY_LIMIT = 15;

    /**
     *  常用值
     */
    const LOG_FIELD_NAME = 'message';
    const INFO_UNIQUE_KEY = 'waybill';

    /**
     * cache configs
     */
    const LAST_QUERY_MAX_ID_DB_CACHE_KEY = 'address:stoCodes:errorLogTest:lastGetMaxQueryId';

    /**
     * 上次请求最大值
     * @var integer
     */
    private $lastQueryMaxId;

    /**
     * 请求时间
     * @var string
     */
    private $queryDate;

    /**
     * 原始错误日志
     * @var array
     */
    private $errorLog;

    /**
     * 数据库日志数据
     * @var array
     */
    private $data;
    /**
     * 请求参数
     * @var array
     */
    private $requestParams;

    /**
     * 请求原始返回
     * @var mixed
     */
    private $requestReturn;

    /**
     * 返回解析结果
     * @var array
     */
    private $requestResult;

    /**
     * 错误的返回
     * @var array
     */
    private $invalidResult;

    private $filePath;

    private $strSource;

    /**
     * @param string $date
     */
    public function do($date = 'yesterday') : void
    {
        $tmp = $this->getLastQueryMaxId();
        $date && $this->date($date);
        $this->getData();
        $this->request();
        $this->handleRequestReturn();
        $this->printOut();
        $this->checkInvalid();
    }

    /**
     * @param string $filePath
     * @param bool $clearOld
     * @return $this
     */
    public function fileSource(string $filePath, $clearOld = true)
    {
        if(file_exists($filePath) && is_readable($filePath)){
            $this->strSource = file_get_contents($this->filePath = $filePath);
            $clearOld && file_put_contents($this->filePath, '');
        }
        return $this;
    }

    /**
     * @param string $str
     * @return $this
     */
    public function strSource(string $str)
    {
        $this->strSource = $str;
        return $this;
    }

    /**
     * @param $date
     * @return $this
     */
    public function date(string $date) : self
    {
        if(!$date || !($dateObj = new DateTime($date))){
            throw new InvalidArgumentException('无效日期');
        }
        if($dateObj < (new DateTime('today'))){
            $this->queryDate = $dateObj->format('Ym');
        }
        return $this;
    }

    /**
     * @return void
     */
    private function printOut()
    {
//        is_array($this->requestResult) && print_r([
//            'requestResultCount' => count($this->requestResult),
//            'requestResult' => $this->requestResult
//        ]);
        echo json_encode($this->requestReturn, JSON_UNESCAPED_UNICODE), PHP_EOL;
        var_dump([
            'requestResultCount' => count($this->requestResult),
            'requestResult' => $this->requestResult,
        ]);
    }

    /**
     * @todo 过滤调返回为空的，港澳台和参数错误的返回为空
     * @return array
     */
    private function checkInvalid()
    {
        if($this->requestResult){
            $assertionFunc = function($val){
                return !empty($val['code1']) && !empty($val['pack_name']);
            };
            $emptyCount = 0;
            foreach ($this->requestResult as $item) {
                if(empty($item)){
                    sprintf('found a empty info: %s' . PHP_EOL, ++$emptyCount);
                }
                $assertionFunc($item) || $this->invalidResult[] = [
                    'request_params_JSON' => json_encode([$item[self::INFO_UNIQUE_KEY] => $this->data[$item[self::INFO_UNIQUE_KEY]]], JSON_UNESCAPED_UNICODE),
                    'request_param_array_format' => $this->data[$item[self::INFO_UNIQUE_KEY]],
                    'result' => $item,
                ];
            }
        }
        var_dump([
            'invalidResultCount' => count($this->invalidResult),
            'invalidResult' => $this->invalidResult,
        ]);
        return $this->invalidResult;
    }

    /**
     * @return mixed
     */
    private function handleRequestReturn()
    {
        return $this->requestReturn ? $this->requestResult = (function (){
            $result =json_decode($this->requestReturn, true);
            return $result['data'];
        })() : [];
    }
    
    /**
     * @return array|null
     */
    private function getData()
    {
        if($this->strSource && ($strResult = json_decode($this->strSource, true)) && json_last_error() === JSON_ERROR_NONE){
            $params = array_column($strResult, 'request_param_array_format');
            $this->data =  array_combine(array_column($params, 'waybill'), $params);
        }else{
            $sql = sprintf(self::QUERY_LOG_TPL, $this->getTableName(), $this->getLastQueryMaxId(), self::QUERY_LIMIT);
            if($data = DbUtil::getInstance(self::DB_NAME)->query($sql)){
                $this->errorLog = $data;
                $allFormattedData = [];
                $maxId = 0;
                foreach ($data as $datum) {
                    if($formattedData = $this->formatLog($datum[self::LOG_FIELD_NAME])){
                        $allFormattedData[$formattedData[self::INFO_UNIQUE_KEY]] = $formattedData;
                    }
                    $datum['id'] > $maxId && $maxId = $datum['id'];
                }
                $this->lastQueryMaxId = (int)$maxId;
                $this->data = $allFormattedData;
            }
        }
        return $this->data;
    }

    /**
     * @return array|mixed
     */
    private function request()
    {
        $params = $this->buildRequestParams();
        return $this->requestReturn = $params ? http::post(self::API_URI, $params, ['TIMEOUT' => 100]) : [];
    }

    /**
     * @return array
     */
    private function buildRequestParams() : array
    {
        $params = [];
        if($this->data){
            $params['data'] = json_encode($this->data);
        }
        return $params;
    }
    
    /**
     * @return string
     */
    private function getTableName() : string
    {
        return $this->queryDate ? self::TABLE_NAME_PREFIX . '_history_' . $this->queryDate : self::TABLE_NAME_PREFIX;
    }

    /**
     * @return int
     */
    private function getLastQueryMaxId() : int
    {
        return $this->lastQueryMaxId ?: ($this->lastQueryMaxId = (int)(CachePool::getCacheInstance()->get(self::LAST_QUERY_MAX_ID_DB_CACHE_KEY) ?: DbCache::getInstance()->get(self::LAST_QUERY_MAX_ID_DB_CACHE_KEY)));
    }

    /**
     * @param int $id
     * @return bool
     */
    private function setLastQueryMaxId(int $id)
    {
        return $id && is_int($id) && CachePool::getCacheInstance()->set(self::LAST_QUERY_MAX_ID_DB_CACHE_KEY, $this->lastQueryMaxId = $id) && DbCache::getInstance()->set(self::LAST_QUERY_MAX_ID_DB_CACHE_KEY, $id);
    }

    /**
     * @param string $str
     * @return array|null
     */
    private function preProcessingLog(string $str) : ?array
    {
        $str = trim(strip_tags($str));
        if (preg_match('#\{.*\}+#sS', $str, $match)) {
            $str = stripslashes($match[0]);
        }
        $arr = json_decode($str, true);
        return json_last_error() === JSON_ERROR_NONE ? $arr : null;
    }


    /**
     * @param string $originStr
     * @return null|string
     */
    public function formatLog(string $originStr) : ?array
    {
        if($data = $this->preProcessingLog($originStr)){
            $toAddress = isset($data['toAddress']) ? $data['toAddress'] : (isset($data['data']) ? $data['data'] : []);
            if(!$toAddress){
                return null;
            }
            $fromAddress = isset($data['fromAddress']) ? $data['fromAddress'] : [];
            $params = [
                'province' => $toAddress['province'] ?? '',
                'city' => $toAddress['city'] ?? '',
                'district' => $toAddress['district'] ?? '',
                'address' => $toAddress['address'] ?? '',
                'from_province' => $fromAddress['province'] ?? '',
                'from_city' => $fromAddress['city'] ?? '',
                'from_district' => $fromAddress['district'] ?? '',
                self::INFO_UNIQUE_KEY => random_int(1000000000, 22222222222222),
            ];
            return $params;
        }
        return null;
    }

    /**
     * @destructor
     */
    public function __destruct()
    {
        empty($this->invalidResult) || file_put_contents(__DIR__.(new DateTime())->format('Y_m_d_H_i_s').'.json', json_encode($this->invalidResult, JSON_UNESCAPED_UNICODE));
        $this->requestResult && $this->setLastQueryMaxId($this->lastQueryMaxId);
    }

    /**
     * for test only
     */
    public function maxIdTest()
    {
        echo $id = $this->getLastQueryMaxId();
        $this->setLastQueryMaxId($id);
    }
}
$times = 20;
$timeCostLog = [];
while($times-- > 0){
    $timeCostLog['start'] = microtime(true);
    (new FailedToGetSto1stCodeLogTest())->do('yesterday');
    $timeCostLog['cost'] = ($timeCostLog['end'] = microtime(true)) - $timeCostLog['start'];
    printf('start@%s, end@ %s, totally cost: %s'.PHP_EOL, ...array_values($timeCostLog));
}

//(new FailedToGetSto1stCodeLogTest())->maxIdTest();

/**
 * @todo USING SplFileObject || SplFileInfo
 * @param $dir
 * @param callable|null $func
 * @return array
 */
function getAllFile($dir, callable $func = null)
{
    $fileList = [];
    if(is_dir($dir) && is_readable($dir)){
        $fileList = array_filter(scandir($dir), function($filePath){
            return $filePath !== '.' && $filePath !== '..';
        });
    }
    return $func ? array_filter($fileList, $func) : $fileList;
}


function setFileWithFullPath(array $fileNameList, string $dir)
{
    return is_dir($dir) ? array_map(function($fileName) use($dir){
        return $dir . $fileName;
    }, $fileNameList) : [];
}

//$dir = 'E:/wwwroot/kuaibao/kuaidihelp_shop2/';
//
//$fileList = setFileWithFullPath(getAllFile($dir, function($fileName){
//    $tmp = explode('.', $fileName);
//    return strpos($fileName, 'test2017') === 0 && end($tmp) === 'json';
//}), $dir);
//
//$clearOldFile = false;
//
//foreach ($fileList as $filePath) {
//    (new FailedToGetSto1stCodeLogTest())->fileSource($filePath, $clearOldFile)->do();
//}
